#include <QHeaderView>
#include <QLabel>
#include <QComboBox>
#include <QPushButton>
#include <QMessageBox>
#include "GlobPref.h"
#include "DataModel.h"
#include "RequestDescriptor.h"
#include "DlgDataEdit.h"
#include "DlgPickDateTime.h"
#include "scImpl.h"

#define FCT "DlgDataEdit::DlgDataEdit"
DlgDataEdit::DlgDataEdit(PkType entityId, PkType pk, QWidget *parent) :
	QDialog(parent), dt(DataModel::get()->getTable(entityId)), pk(pk), transId(0),
	cBoxFilled(false), ignoreModify(true), dtIks(0), dtIks_(0)
{
	ctrc(Trc::WORK,APP,FCT)<<"entity "<<entityId<<" pk "<<pk<<endl;
	//ui.setupUi(this); nope - handcrafted
	resize(195, 138);
	setWindowTitle(pk==PK_UNDEF?tr("New Data"):tr("Edit Data"));
	pickMapper = new QSignalMapper(this);
	connect(pickMapper, SIGNAL(mapped(int)), this, SLOT(pickValueSl(int)));
	verticalLayout=new QVBoxLayout(this);
	gridLayout=new QGridLayout;
	DOsPkIdx::iterator dit;
	if(pk==PK_UNDEF)
	{
		dit=dt.getPayload().dos.get<DataObject::PkTag>().end();
		deadl=DataModel::get()->getDeadlineUTC();
	}
	else
	{
		dit=dt.getPayload().dos.get<DataObject::PkTag>().find(pk);
		switch(dt.getDomain())
		{
		case DOMAIN_DATATL:
			deadl=dit->getIks();
			break;
		case DOMAIN_DATAVS:
			// TODO feature DOMAIN_DATAVS
			break;
		default:
			deadl=DataModel::get()->getDeadlineUTC();
			break;
		}
	}
	const ColDescs & attrs(dt.getPayload().attrs);
	int line(0);
	RequestDescriptor *rd(new RequestDescriptor);
	for(unsigned int i(0); i<attrs.size(); ++i)
	{
		if(!GlobPref::get()->getDebugData() &&
			(attrs.at(i).funct==AFUNCT_PK || attrs.at(i).funct==AFUNCT_IK || attrs.at(i).funct==AFUNCT_LUT))
			continue;
		QLabel *label(new QLabel(attrs.at(i).name, this));
		gridLayout->addWidget(label, line, 0);
		if(attrs.at(i).dtype==ADTYPE_DATIM)
		{
			QDateTimeEdit *dtEdit(new QDateTimeEdit(this));
			if(pk!=PK_UNDEF)
				dtEdit->setDateTime(dit->getPayload().at(i).getQDateTime());
			label->setBuddy(dtEdit);
			connect(dtEdit, SIGNAL(dateTimeChanged(const QDateTime &)), this, SLOT(isModifiedSl()));
			attrMap.insert(AttrMap::value_type(i, dtEdit));
			gridLayout->addWidget(dtEdit, line, 1);
			if(attrs.at(i).funct==AFUNCT_DATA || attrs.at(i).funct==AFUNCT_IKS || attrs.at(i).funct==AFUNCT_IKS2)
			{
				QPushButton *but(new QPushButton(tr("set..."), this));
				pickMapper->setMapping(but, i);
				connect(but, SIGNAL(clicked()), pickMapper, SLOT(map()));
				gridLayout->addWidget(but, line, 2);
			}
			else
				dtEdit->setEnabled(false);
			switch(attrs.at(i).funct)
			{
			case AFUNCT_IKS: dtIks=dtEdit; break;
			case AFUNCT_IKS2: dtIks_=dtEdit; break;
			default: ;
			}
		}
		else if(attrs.at(i).dtype==ADTYPE_DBKEY &&
				(attrs.at(i).funct==AFUNCT_FIK || attrs.at(i).funct==AFUNCT_FPK))
		{
			QComboBox *cb(new QComboBox(this));
			cb->addItem(tr("-reading data-"), QVariant(pk==PK_UNDEF?PK_UNDEF:dit->getPayload().at(i).getDbKey()));
			if(attrs.at(i).funct==AFUNCT_FPK)
				rd->include(Request(attrs.at(i).target));
			else
				// TODO feature - cross-domain referencing
				// dit->getIks()?dit->getIks():DataModel::get()->getDeadlineUTC()
				rd->include(Request(attrs.at(i).target, deadl, false));
			label->setBuddy(cb);
			connect(cb, SIGNAL(currentIndexChanged(int)), this, SLOT(isModifiedSl()));
			attrMap.insert(AttrMap::value_type(i, cb));
			gridLayout->addWidget(cb, line, 1, 1, 2);
		}
		else
		{
			QLineEdit *lineEdit(new QLineEdit(this));
			if(pk!=PK_UNDEF)
				lineEdit->setText(dit->getPayload().at(i).getAsQString(true));
			label->setBuddy(lineEdit);
			connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(isModifiedSl()));
			attrMap.insert(AttrMap::value_type(i, lineEdit));
			gridLayout->addWidget(lineEdit, line, 1, 1, 2);
			if(attrs.at(i).funct!=AFUNCT_DATA)
				lineEdit->setEnabled(false);
		}
		++line;
	}
	verticalLayout->addLayout(gridLayout);
	verticalLayout->addItem(new QSpacerItem(20, 32, QSizePolicy::Minimum, QSizePolicy::Expanding));
	QHBoxLayout * butBox = new QHBoxLayout();
	butBox->setObjectName(QString::fromUtf8("horizontalLayout"));
	butBox->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
	butOk = new QPushButton(this);
	butOk->setObjectName(QString::fromUtf8("butOk"));
    butOk->setText(tr("OK"));
	butOk->setEnabled(false);
	connect(butOk, SIGNAL(clicked()), this, SLOT(accept()));
	butBox->addWidget(butOk);
	butCancel = new QPushButton(this);
	butCancel->setObjectName(QString::fromUtf8("butCancel"));
    butCancel->setText(tr("Cancel"));
	connect(butCancel, SIGNAL(clicked()), this, SLOT(reject()));
	butBox->addWidget(butCancel);
	verticalLayout->addLayout(butBox);
	QMetaObject::connectSlotsByName(this);
	if(rd->empty())
	{
		delete rd;
		cBoxFilled=true;
	}
	else
	{
		connect(DataModel::get(), SIGNAL(dataChangedSig(unsigned long)), this, SLOT(fillComboBoxSl(unsigned long)));
		transId=DataModel::get()->requestData(rd);
		if(!transId)
			fillComboBoxSl(0);
	}
	ignoreModify=false;
}

#undef FCT
#define FCT "DlgDataEdit::~DlgDataEdit"
DlgDataEdit::~DlgDataEdit()
{
}

#undef FCT
#define FCT "DlgDataEdit::fillComboBoxSl"
void DlgDataEdit::fillComboBoxSl(unsigned long transactionId)
{
	if(transactionId && transactionId!=transId)
	{
		ctrc(Trc::LOOP,APP,FCT)<<"skipped trans "<<transactionId<<" waiting for "<<transId<<endl;
		return;
	}
	if(transactionId)
		transId=0;
	if(cBoxFilled)
		return;
	cBoxFilled=true;
	ignoreModify=true;
	//DOsPkIdx::iterator dit(dt.getPayload().dos.get<DataObject::PkTag>().find(pk));
	const ColDescs & attrs(dt.getPayload().attrs);
	for(AttrMap::const_iterator it(attrMap.begin()); it!=attrMap.end(); ++it)
	{
		int colidx(it->first);
		if(attrs.at(colidx).dtype!=ADTYPE_DBKEY ||
				(attrs.at(colidx).funct!=AFUNCT_FIK && attrs.at(colidx).funct!=AFUNCT_FPK))
			continue;
		QComboBox * cb(dynamic_cast<QComboBox *>(it->second));
		PkType usePIk(cb->itemData(0).toLongLong());
		cb->clear();
		const DataTable & dttgt(DataModel::get()->getTable(attrs.at(colidx).target));
		int niceCol(dttgt.getPayload().niceNameCol);
		int setIdx(0);
		switch(dttgt.getDomain())
		{
		case DOMAIN_DATATL:
		{
			DOsCIkIdx & idx(dttgt.getPayload().dos.get<DataObject::IkTag>());
			PkType lastIk(0);
			int i(0);
			for(DOsCIkIdx::const_iterator it(idx.begin()); it!=idx.end(); ++it)
			{
				PkType newIk(it->getIk());
				if(lastIk!=newIk && it->getIks()<=deadl && it->getIks2()>deadl)
				{
					ctrc(Trc::LOOP,APP,FCT)<<"map row "<<i<<" to IK "<<newIk<<" PK "<<it->getPk()<<endl;
					cb->addItem(it->getPayload().at(niceCol).getAsQString(true), QVariant(it->getPk()));
					lastIk=newIk;
					if((attrs.at(colidx).funct==AFUNCT_FIK && newIk==usePIk) ||
							(attrs.at(colidx).funct==AFUNCT_FPK && it->getPk()==usePIk))
						setIdx=i;
					++i;
				}
			}
			break;
		}
		case DOMAIN_DATAVS:
		{
			// TODO feature DOMAIN_DATAVS
			break;
		}
		default:
		{
			DOsCPkIdx & idx(dttgt.getPayload().dos.get<DataObject::PkTag>());
			int i(0);
			for(DOsCPkIdx::const_iterator it(idx.begin()); it!=idx.end(); ++it, ++i)
			{
				ctrc(Trc::LOOP,APP,FCT)<<"map row "<<i<<" to PK "<<it->getPk()<<endl;
				cb->addItem(it->getPayload().at(niceCol).getAsQString(true), QVariant(it->getPk()));
				if(it->getPk()==usePIk)
					setIdx=i;
				++i;
			}
			break;
		}
		}
		cb->setCurrentIndex(setIdx);
	}
	ignoreModify=false;
}

#undef FCT
#define FCT "DlgDataEdit::accept"
void DlgDataEdit::accept()
{
	// TODO NOW - insert dataset
	DOsPkIdx::iterator dit(dt.getPayload().dos.get<DataObject::PkTag>().find(pk));
	if(dtIks && dtIks_) // equivalent to dt.getDomain()==DOMAIN_DATATL
	{
		if(dtIks_->dateTime()<dtIks->dateTime())
		{
			QMessageBox::warning(this, tr("Data Consistency Check"), tr("valid-to must be greater or equal valid-from"), QMessageBox::Ok, QMessageBox::Ok);
			return;
		}
	}
	const ColDescs & attrs(dt.getPayload().attrs);
	for(AttrMap::const_iterator it(attrMap.begin()); it!=attrMap.end(); ++it)
	{
		int idx(it->first);
		if(!it->second->isEnabled())
			continue; // no updates for read-only fields
		switch(attrs.at(idx).dtype)
		{
		case ADTYPE_STRASC:
		case ADTYPE_STRUTF8:
		case ADTYPE_I32S:
		case ADTYPE_F64:
		case ADTYPE_UNDEF:
		{
			QLineEdit * lineEdit(dynamic_cast<QLineEdit *>(it->second));
			dit->getPayload().at(idx).tryUpdate(lineEdit->text(), true);
			break;
		}
		case ADTYPE_DBKEY:
		{
			QComboBox * cb(dynamic_cast<QComboBox *>(it->second));
			dit->getPayload().at(idx)=cb->itemData(cb->currentIndex()).toLongLong();
			break;
		}
		case ADTYPE_DATIM:
		{
			QDateTimeEdit * dtEdit(dynamic_cast<QDateTimeEdit *>(it->second));
			dit->getPayload().at(idx)=dtEdit->dateTime();
			break;
		}
		}
	}
	dt.getPayload().makeConsistent(dit);
	RequestDescriptor * rd(new RequestDescriptor(Request(dt.getPk(), pk)));
	DataModel::get()->requestData(rd);
	// TODO 3 bug - errors during update are not handled
	QDialog::accept();
}

#undef FCT
#define FCT "DlgDataEdit::pickValueSl"
void DlgDataEdit::pickValueSl(int col)
{
	ctrc(Trc::WORK,APP,FCT)<<"col "<<col<<endl;
	QWidget* myWid(attrMap.find(col)->second);
	if(QDateTimeEdit *dtEdit=dynamic_cast<QDateTimeEdit *>(myWid))
	{
		DlgPickDateTime dlg(dtEdit->dateTime(), this);
		if(dlg.exec()==QDialog::Rejected)
		{
			ctrc(Trc::WORK,APP,FCT)<<"dialog canceled"<<endl;
			return;
		}
		dtEdit->setDateTime(dlg.getResult());
	}
	else
		ctrc(Trc::SETUP,APP,FCT)<<"don't know which type to pick"<<endl;
}

#undef FCT
#define FCT "DlgDataEdit::isModifiedSl"
void DlgDataEdit::isModifiedSl()
{
	if(ignoreModify)
		return;
	butOk->setEnabled(true);
}
